package com.maxd.utils;

import com.maxd.model.CommonInfo;
import com.maxd.respository.common.CommonJpaRepository;
import com.maxd.respository.common.CommonRepository;
import com.maxd.utils.wx.HttpClientSSL;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.jdom.Document;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.lang.reflect.Method;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;

@Slf4j
@Component
public class RSAUtils {

    private static final String TRANS_URL = "https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank";

    private static RequestConfig requestConfig;

    // 连接超时时间，默认10秒
    private static int socketTimeout = 10000;

    // 传输超时时间，默认30秒
    private static int connectTimeout = 30000;

    private static CommonRepository commonRepository;
    private static CommonJpaRepository commonJpaRepository;

    @Autowired
    public void setCommonRepository(CommonRepository commonRepository,CommonJpaRepository commonJpaRepository){
        RSAUtils.commonRepository=commonRepository;
        RSAUtils.commonJpaRepository=commonJpaRepository;
    }

    public static String weChatWithdrawal(String data) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        InputStream instream = null;
        try {
            ClassPathResource classPathResource = new ClassPathResource("weixin/apiclient_cert.p12");
            instream = classPathResource.getInputStream();
        } catch (IOException var10) {
        }

        if (instream == null) {
            return null;
        }
        String sslPassword=commonRepository.findOne(76).getValue();
        String result = "";
        try {
            keyStore.load(instream, sslPassword.toCharArray());
        } finally {
            instream.close();
        }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, sslPassword.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        try {
            HttpPost httppost = new HttpPost(TRANS_URL);
            StringEntity entitys = new StringEntity(data,"UTF-8");
            httppost.setEntity((HttpEntity) entitys);
            // 根据默认超时限制初始化requestConfig
            requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
            // 设置请求器的配置
            httppost.setConfig(requestConfig);
            CloseableHttpResponse response = httpclient.execute(httppost);
            try {
                HttpEntity entity = response.getEntity();
                result = EntityUtils.toString(entity,"UTF-8");
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
        return result;
    }


    public static String selectChatWithdrawal(String data) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        InputStream instream = null;
        try {
            ClassPathResource classPathResource = new ClassPathResource("weixin/apiclient_cert.p12");
            instream = classPathResource.getInputStream();
        } catch (IOException var10) {
        }

        if (instream == null) {
            return null;
        }
        String sslPassword=commonRepository.findOne(76).getValue();
        //String sslPassword="1576253451";
        String result = "";
        try {
            keyStore.load(instream, sslPassword.toCharArray());
        } finally {
            instream.close();
        }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, sslPassword.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        try {
            HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/mmpaysptrans/query_bank");
            StringEntity entitys = new StringEntity(data,"UTF-8");
            httppost.setEntity((HttpEntity) entitys);
            // 根据默认超时限制初始化requestConfig
            requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
            // 设置请求器的配置
            httppost.setConfig(requestConfig);
            CloseableHttpResponse response = httpclient.execute(httppost);
            try {
                HttpEntity entity = response.getEntity();
                result = EntityUtils.toString(entity,"UTF-8");
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
        return result;
    }


    /**
     * @param params
     * @Author: WQY
     * @Description:请求值转换为xml格式 SortedMap转xml
     * @Date: 2017-9-7 17:18
     */
    public static String SortedMaptoXml(SortedMap<String, String> params) {
        StringBuilder sb = new StringBuilder();
        Set es = params.entrySet();
        Iterator it = es.iterator();
        sb.append("<xml>\n");
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            sb.append("<" + k + ">");
            sb.append(v);
            sb.append("</" + k + ">\n");
        }
        sb.append("</xml>");
        return sb.toString();
    }



    public static byte[] decrypt(byte[] encryptedBytes, PrivateKey privateKey, int keyLength, int reserveSize, String cipherAlgorithm) throws Exception {
        int keyByteSize = keyLength / 8;
        int decryptBlockSize = keyByteSize - reserveSize;
        int nBlock = encryptedBytes.length / keyByteSize;
        ByteArrayOutputStream outbuf = null;
        try {
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            outbuf = new ByteArrayOutputStream(nBlock * decryptBlockSize);
            for (int offset = 0; offset < encryptedBytes.length; offset += keyByteSize) {
                int inputLen = encryptedBytes.length - offset;
                if (inputLen > keyByteSize) {
                    inputLen = keyByteSize;
                }
                byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, inputLen);
                outbuf.write(decryptedBlock);
            }
            outbuf.flush();
            return outbuf.toByteArray();
        } catch (Exception e) {
            throw new Exception("DEENCRYPT ERROR:", e);
        } finally {
            try{
                if(outbuf != null){
                    outbuf.close();
                }
            }catch (Exception e){
                outbuf = null;
                throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);
            }
        }
    }
    public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm) throws Exception {
        int keyByteSize = keyLength / 8;
        int encryptBlockSize = keyByteSize - reserveSize;
        int nBlock = plainBytes.length / encryptBlockSize;
        if ((plainBytes.length % encryptBlockSize) != 0) {
            nBlock += 1;
        }
        ByteArrayOutputStream outbuf = null;
        try {
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);

            outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);
            for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
                int inputLen = plainBytes.length - offset;
                if (inputLen > encryptBlockSize) {
                    inputLen = encryptBlockSize;
                }
                byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
                outbuf.write(encryptedBlock);
            }
            outbuf.flush();
            return outbuf.toByteArray();
        } catch (Exception e) {
            throw new Exception("ENCRYPT ERROR:", e);
        } finally {
            try{
                if(outbuf != null){
                    outbuf.close();
                }
            }catch (Exception e){
                outbuf = null;
                throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);
            }
        }
    }
    public static PrivateKey getPriKey(String privateKeyPath,String keyAlgorithm){
        PrivateKey privateKey = null;
        InputStream inputStream = null;
        try {
            if(inputStream==null){
                System.out.println("hahhah1!");
            }
            inputStream = new FileInputStream(privateKeyPath);
            privateKey = getPrivateKey(inputStream,keyAlgorithm);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null){
                try {
                    inputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        return privateKey;
    }
    public static PublicKey getPubKey(){
        PublicKey publicKey = null;
        InputStream inputStream = null;
        try
        {
            //inputStream = ResourceUtils.getURL(publicKeyPath).openStream();
            publicKey = getPublicKey("RSA");
        } catch (Exception e) {
            e.printStackTrace();//EAD PUBLIC KEY ERROR
        } finally {
            if (inputStream != null){
                try {
                    inputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        return publicKey;
    }
    public static PublicKey getPublicKey( String keyAlgorithm) throws Exception {
        try
        {

            /*BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String readLine = null;
            while ((readLine = br.readLine()) != null) {
                sb.append(readLine);
            }*/
            /*String publicKeyString="MIIBCgKCAQEArleg812Aus2RZZKaYX1qTOOEDwjbGJyexhpTsPQIA6dEBI8yiW9C" +
                    "JVdq8LXq+yOWaFtJJ0GxLpi/BR9w6g/a8XUNS5RkEr+s+ZVw3RYacABxRBsDLJSk" +
                    "LLwBweXFNuGULvio6b9WKk1iVZKI1UHPK+SU0tZ6JtuqEVPNAk5E4JNOUBCoXdKX" +
                    "Efu1q38/jNLJ9P7JmUd+7ZDJExmi3clpfEMWenq9zHPaHuMOhKUUfT1Aa1BMQIuV" +
                    "CjoZ+Pb+42+A1s1AVhoShrJtAXpI6txBL3dYiecd8OHxrRkZCgceq8G5TlpC6gdi" +
                    "4NkAU5ySN7JzQUU0YigICuR/r74r33oSRQIDAQAB";*/
            CommonInfo key = commonRepository.findOne(301);
            String publicKeyString=key.getValue();
            if(StringUtils.isEmpty(publicKeyString)){
                publicKeyString = getRSA();
                if(StringUtils.isNotEmpty(publicKeyString)){
                    key.setValue(publicKeyString);
                    commonJpaRepository.save(key);
                }
            }
            org.bouncycastle.asn1.pkcs.RSAPublicKey rsaPublicKey =  org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(
                    org.bouncycastle.util.encoders.Base64.decode(publicKeyString));

            java.security.spec.RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent());

            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            //下行出错  java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=127, too big.
            PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
            return publicKey;
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("READ PUBLIC KEY ERROR:", e);
        }
    }
    public static PrivateKey getPrivateKey(InputStream inputStream, String keyAlgorithm) throws Exception {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String readLine = null;
            while ((readLine = br.readLine()) != null) {
                if (readLine.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(readLine);
                    sb.append('\r');
                }
            }
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(decodeBase64(sb.toString()));
            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            PrivateKey privateKey = keyFactory.generatePrivate(priPKCS8);
            return privateKey;
        } catch (Exception e) {
            throw new Exception("READ PRIVATE KEY ERROR:" ,e);
        }  finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                inputStream = null;
                throw new Exception("INPUT STREAM CLOSE ERROR:", e);
            }
        }
    }
    //一下面是base64的编码和解码
    public static String encodeBase64(byte[]input) throws Exception{
        Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
        Method mainMethod= clazz.getMethod("encode", byte[].class);
        mainMethod.setAccessible(true);
        Object retObj=mainMethod.invoke(null, new Object[]{input});
        return (String)retObj;
    }
    /***
     * decode by Base64
     */
    public static byte[] decodeBase64(String input) throws Exception{
        Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
        Method mainMethod= clazz.getMethod("decode", String.class);
        mainMethod.setAccessible(true);
        Object retObj=mainMethod.invoke(null, input);
        return (byte[])retObj;
    }

    private static String payUrl = "https://fraud.mch.weixin.qq.com/risk/getpublickey";

    public static String getRSA() {
        String KEY = commonRepository.findOne(75).getValue();
        String mchId = commonRepository.findOne(76).getValue();
        Map<String, String> parameterMap = new HashMap<>();
        parameterMap.put("mch_id", mchId);
        parameterMap.put("nonce_str", generateNonceStr());
        try {

            parameterMap.put("sign_type", "MD5");
            parameterMap.put("sign", HttpClientSSL.generateSignature(parameterMap, KEY, "MD5"));
            //要以xml格式传递
            String postDataXML = HttpClientSSL.mapToXml(parameterMap);
            String result = HttpClientSSL.wxRefundLink(postDataXML, mchId);

            SAXBuilder saxBuilder = new SAXBuilder();
            Document doc = saxBuilder.build(new StringReader(result));
            return doc.getRootElement().getChild("pub_key").getValue();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("读取证书异常："+e.getMessage(),e);
        }
        return null;
    }

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();

    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }

}
